<?php
/**
 * Note:
 * 支付时, 随机选一个可用payChannel, 使用其配置;
 * 通知时, 先查询订单信息, 根据订单中的 payChannel, 进行后续校验(查订单等);
 * 要求各个商户配置的接口密钥相同, 方便前期的校验通过.
 */
namespace App\Services\Pay;

use App\Abstracts\PayAbstract;
use App\Caches\Game\AppSourceCache;
use App\Caches\Game\PayChannelCache;
use App\Exceptions\PayChannelEmptyException;
use App\Models\Game\Center\PayOrder;
use App\Models\Game\Center\WithdrawOrder;
use Illuminate\Support\Str;
use Yansongda\Pay\Exceptions\GatewayException;
use Yansongda\Pay\Exceptions\InvalidArgumentException;
use Yansongda\Pay\Exceptions\InvalidSignException;
use Yansongda\Pay\Pay;

class PayWechat extends PayAbstract
{
    public const CHANNEL = 'wechat';

    /**
     * @var \Yansongda\Pay\Provider\Wechat
     */
    public $payment;

    /**
     * 获取网关
     * @param $order
     * @return \Yansongda\Pay\Provider\Wechat
     * @throws PayChannelEmptyException
     */
    public function getPaymentByOrder($order): \Yansongda\Pay\Provider\Wechat
    {
        $appSource = AppSourceCache::get($order->source);
        if ($order instanceof PayOrder) {
            $this->payChannel = $this->getUseablePayChannel(self::CHANNEL, $order->price, $appSource->type);
        } else if ($order instanceof WithdrawOrder) {
            $this->payChannel = $this->getUseableWithdrawChannel(self::CHANNEL, $order->money, $appSource->type);
        } else {
            throw new \Exception('invalid param: order');
        }
        $config = $this->getConfig($this->payChannel);
        $this->payment = Pay::wechat($config);
        return $this->payment;
    }

    /**
     * 根据 channelId 获取 payment
     * @param int $payChannelId
     * @return \Yansongda\Pay\Provider\Wechat
     */
    public function getPaymentByPayChannelId(int $payChannelId): \Yansongda\Pay\Provider\Wechat
    {
        $this->payChannel = PayChannelCache::get($payChannelId);
        $config = $this->getConfig($this->payChannel);
        $this->payment = Pay::wechat($config);
        return $this->payment;
    }

    /**
     * 获取测试权益实例
     * @return \Yansongda\Pay\Provider\Wechat
     * @throws \Exception
     */
    public function getGatewayDev(): \Yansongda\Pay\Provider\Wechat
    {
        $payChannels = PayChannelCache::getChannels(self::CHANNEL);
        $this->payChannel = null;
        foreach ($payChannels as $payChannel) {
            if ($payChannel->sandbox) {
                $this->payChannel = $payChannel;
                break;
            }
        }
        if (!$this->payChannel) throw new PayChannelEmptyException('无可用沙盒支付通道');
        $config = $this->getConfig($this->payChannel);
        $this->payment = Pay::wechat($config);
        return $this->payment;
    }

    /**
     * 校验
     * @param PayOrder $payOrder
     * @return bool
     */
    public function payOrderVerify(PayOrder $payOrder): bool
    {
        try {
            // $order = $payOrder->orderid;
            $order = ['out_trade_no' => $payOrder->orderid];

            // APP或小程序订单需要传入type
            if ($payOrder->gateway === 'app' || $payOrder->gateway === 'miniapp') {
                $order['type'] = $payOrder->gateway;
            }
            // 设置了子商户, 需要传入 sub_mch_id
            if (!empty($this->config['sub_mch_id'])) $order['sub_mch_id'] = $this->config['sub_mch_id'];

            $orderData = $this->payment->find($order, $payOrder->gateway);
            if (!in_array($orderData->result_code, ['SUCCESS'])) {
                throw new \Exception('订单状态校验失败');
            }
        } catch (\Exception $e) {
            report($e);
            return false;
        }
        return true;
    }

    /**
     * 权益商户
     * @param string|null $subCode
     * @param string|null $subMsg
     * @return bool
     */
    public function withdrawMerchantFailed(string $subCode = null, string $subMsg = null): bool
    {
        if (!$subCode && !$subMsg) return false;
        $subCodes = [
            'NO_AUTH',
            'NOTENOUGH',
            'MONEY_LIMIT',
            'SEND_MONEY_LIMIT',
        ];
        if (in_array($subCode, $subCodes)) return true;

        // $subMsgs = ['余额不足', '额度超限', '超出单日转账限额'];
        // if (Str::contains($subMsg, $subMsgs)) return true;

        return false;
    }

    /**
     * 权益用户
     * @param string|null $subCode
     * @param string|null $subMsg
     * @return bool
     */
    public function withdrawUserFailed(string $subCode = null, string $subMsg = null): bool
    {
        if (!$subCode && !$subMsg) return false;
        $subCodes = [
            'NAME_MISMATCH',
        ];
        if (in_array($subCode, $subCodes)) return true;

        $subMsgs = ['收款', '用户'];
        if (Str::contains($subMsg, $subMsgs)) return true;

        return false;
    }

    /**
     * 设置了分账比例, 且订单金额 >= 1元
     * @param int $price
     * @return bool
     */
    public function needProfitSharing(int $price): bool
    {
        $scale = $this->config['profit_sharing_scale'] ?? 0;
        $first = $scale > 0;
        $second = $price >= 100;// 金额超过1元
        return $first && $second;
    }

    /**
     * 权益订单检查
     * @param WithdrawOrder $withdrawOrder
     * @return array status reason torderid
     * @throws InvalidSignException
     * @throws GatewayException
     * @throws InvalidArgumentException
     */
    public function withdrawOrderQuery(WithdrawOrder $withdrawOrder): array
    {
        $payment = $this->getPaymentByPayChannelId($withdrawOrder->pay_channel_id);

        $result = $payment->find($withdrawOrder->orderid, 'transfer');

        $reason = $result->reason ?: '';
        $torderid = $result->payment_no ?? '';

        // 处理中视为处理中
        if ($result->status === 'PROCESSING') {
            return [WithdrawOrder::STATUS_PROCESSING, $reason, $torderid];
        } else if ($result->status === 'FAIL') {
            return [WithdrawOrder::STATUS_FAILED, $reason, $torderid];
        }
        return [WithdrawOrder::STATUS_SUCCESS, $reason, $torderid];
    }
}
